package services.freenas

import models.users._

import javax.inject.Inject
import play.api.libs.json._
import play.api.Logging
import security.access.PermissionAccess

import scala.collection.mutable.ArrayBuffer
import scala.concurrent.{ExecutionContext, Future}

class FreeNASManage @Inject()(np: FreeNASPost, nasRepo: AppNasUsersRepository)(implicit exec: ExecutionContext) extends Logging {

  private def pinyin(pinYinStr: String) = {
    import com.github.stuxuhai.jpinyin.{PinyinFormat, PinyinHelper}
    var py = PinyinHelper.convertToPinyinString(pinYinStr, "", PinyinFormat.WITHOUT_TONE)
    if (py.length > 16) {
      val py1 = PinyinHelper.convertToPinyinString(pinYinStr, " ", PinyinFormat.WITHOUT_TONE).split(" ")
      val py2 = PinyinHelper.getShortPinyin(pinYinStr)
      py = py1(0) + py2.tail
    }
    if (py.length > 16) {
      py = PinyinHelper.getShortPinyin(pinYinStr)
    }
    py
  }

  def groupList = {
    val res = np.get("/group", "")
    res.map(r => {
      val i = r.json.as[JsArray].value.iterator
      while (i.hasNext) {
        println(i.next())
      }
      "groupList"
    })
  }

  private def groupCreateToDb(name: String, pname: String) = {
    nasRepo.nasGroupCreate(nas_name = name, pname = pname)
  }

  private def groupCreateToNas(id: Int, name: String) = {
    /**
     * {
     * "gid": 0,
     * "name": "string",
     * "smb": true,
     * "sudo": true,
     * "allow_duplicate_gid": true,
     * "users": [
     * 0
     * ]
     * }
     */
    val group = Json.obj(
      "gid" -> (1_000_000 + id),
      "name" -> name,
      "smb" -> true,
      "sudo" -> false)
    logger.debug(s"nas groupCreate ($name,  $group)")
    np.post("/group", group)
      .flatMap { r =>
        if (r.body.length < 8) {
          r.body.toInt
          nasRepo.nasGroupIdUpdate(id, r.body.toInt)
        } else {
          logger.debug("nas groupCreate faulied: " + r.body)
          Future(0)
        }
      }
  }

  def groupCreate(pname: String) = {
    import com.github.stuxuhai.jpinyin.{PinyinFormat, PinyinHelper}
    val nas_group_name = PinyinHelper.convertToPinyinString(pname, "_", PinyinFormat.WITHOUT_TONE)
    groupCreateToDb(nas_group_name, pname)
      .flatMap { id =>
        groupCreateToNas(id.getOrElse(0), nas_group_name)
      }
  }

  def groupUpdate = {
    /**
     *
     */

  }

  case class Group(id: Int, group_id: Int, nas_name: String)

  private def groupGetFromNas = {
    val groups = ArrayBuffer[Group]()
    val res = np.get("/group", "")
    res.map(r => {
      val i = r.json.as[JsArray].value.iterator
      while (i.hasNext) {
        val group = i.next().as[JsObject].value
        // {"id":44,"gid":546,"group":"builtin_guests","builtin":true,"sudo":false,"smb":true,"users":[],"local":true,"id_type_both":false}
        groups.append(Group(
          id = group("gid").as[Int] - 1_000_000,
          group_id = group("id").as[Int],
          nas_name = group("group").as[String]
        ))

      }
      groups.toSet
    })
  }

  private def groupGetFromDb = {
    val groups = ArrayBuffer[Group]()
    nasRepo.nasGroupAll.map {
      r => {
        val i = r.iterator
        while (i.hasNext) {
          val group = i.next()
          groups.append(Group(
            id = group.id.getOrElse(0),
            group_id = group.nas_id,
            nas_name = group.nas_name
          ))
        }
        groups.toSet
      }
    }
  }

  def groupCheck = {
    groupGetFromNas.flatMap { nas => {
      groupGetFromDb.map { db => {
        val publicSet = nas & db
        val nasOnlySet = nas &~ publicSet
        val dbOnlySet = db &~ publicSet
        // nas only
        nasOnlySet.filter(_.id > 1_000_000).filter(_.id < 2_000_000).foreach { f => nasRepo.nasGroupIdUpdate(f.id, f.group_id) }
        // db only
        val dbonly = dbOnlySet.filter(_.group_id == 0)
        dbonly.foreach(f => groupCreateToNas(f.id, f.nas_name))
        dbonly.mkString(",")
      }
      }
    }
    }
  }


  def groupDelete = {}

  def userList = {
    val res = np.get("/user", "")
    res.map(r => {
      val i = r.json.as[JsArray].value.iterator
      while (i.hasNext) {
        println(i.next())
      }
      "userList"
    })
  }

  private def userCreateToNas(id: Int, name: String, password: String, gid: Int) = {
    logger.debug(s"nas user create $id,$name")
    np.post("/user", Json.obj(
      "uid" -> (1_000_000 + id),
      "username" -> name,
      "group" -> gid,
      "group_create" -> false,
      "home" -> s"/mnt/datas/home/$name",
      "home_mode" -> "700",
      "full_name" -> name,
      "password" -> password,
      "password_disabled" -> false,
      "locked" -> false,
      "microsoft_account" -> false,
      "smb" -> true,
      "sudo" -> false,
      "shell" -> "/usr/sbin/nologin"
    ))
      .flatMap { r =>
        if (r.body.length < 8) {
          r.body.toInt
          nasRepo.nasUserIdUpdate(id, r.body.toInt)
        } else {
          logger.error(s"userCreateToNas failure:${r.body}")
          Future(0)
        }
      }
  }

  private def userCreateToDb(nasUser: NasUser) = {
    nasRepo.nasUserCreate(nasUser)
  }

  def userUpdate(id: Int, name: String, password: String, group: String) = {
    nasRepo.nasGroupFindBypName(group).flatMap { g =>
      val group = if (g.isEmpty) (1,45) //(0, np.freeNASConfig.gid)
      else (g.head.id.getOrElse(0), g.head.nas_id)
      val nasUser = NasUser(id = id, nas_name = name, group_id = group._1)
      logger.debug(s"userUpdate $name,id: $nasUser,group:${group._1},${group._2}")
      userCreateToDb(NasUser(id = id, nas_name = name, group_id = group._1))
        .flatMap { user =>
          logger.error(s"userUpdate $user,group:${group._1},${group._2}")
          user match {
            case 0 => userPasswordChangeToNas(id, password)
            case -1 => Future("用户名重复，请修改！")
            case 1 => userCreateToNas(id, name, password, group._2).map { id =>
              if (id == 0) "用户创建或更新失败！" else "用户创建或更新成功！"
            }
            case _ => Future("未知道错误，请联系管理员！")
          }
        }

    }


  }

  def userUpdate1(id: Int, name: String, password: String,group:String) = {
    nasRepo.nasGroupFindBypName(group).flatMap { g =>
      val group = if (g.isEmpty) (0, np.freeNASConfig.gid)
      else (g.head.id.getOrElse(0), g.head.nas_id)
      logger.debug(s"userUpdate $name,group:${group._1},${group._2}")
      userCreateToDb(NasUser(id = id, nas_name = name, group_id = group._1))
        .flatMap { user =>
          logger.error(s"userUpdate $user,group:${group._1},${group._2}")

          if (user == 0) {
            userPasswordChangeToNas(id, password)
          } else if (user < 0) {
            Future(false)
          } else {
            userCreateToNas(id, name, password, group._2).map { id =>
              if (id == 0) false else true
            }
          }
        }

    }


  }


  def userGroupUpdate(id: Int, gid: Int) = {
    nasRepo.userGetNasUser(id).flatMap {
      ur =>
        if (ur.isEmpty) {
          Future(false)
        } else {
          val nasid = ur.head.nas_id
          nasRepo.nasGroupFindBy(gid).flatMap {
            n => {
              val nas_id = if (n.id.nonEmpty) n.nas_id else np.freeNASConfig.gid
              val res = np.put(s"/user/id/$nasid", Json.obj("group" -> nas_id))
              res.map { r =>
                if (r.body.length < 8) {
                  nasRepo.nasUserGidUpdate(id, gid)
                  r.body.toInt == nasid
                } else {
                  logger.error(r.body)
                  false
                }
              }
            }
          }

        }
    }

  }

  private def userPasswordChangeToNas(id: Int, pwds: String) = {
    nasRepo.userGetNasUser(id).flatMap {
      ur =>
        if (ur.isEmpty) {
          Future("共享密码更新失败！查找不到这个用户。")
        } else {
          val nasid = ur.head.nas_id
          val res = np.put(s"/user/id/$nasid", Json.obj("password" -> pwds))
          res.map { r =>
            if (r.body.length < 8) {
              r.body.toInt == nasid
              if (r.body.toInt == nasid) "共享密码更新成功！" else "共享密码更新失败！id错误。"
            } else {
              logger.error(r.body)
              "共享密码更新失败！请查看日志。"
            }
          }
        }
    }
  }

  def userLocked(id: Int) = {
    nasRepo.userGetNasUser(id).flatMap {
      ur =>
        if (ur.isEmpty) {
          Future(false)
        } else {
          val nasid = ur.head.nas_id
          val nasName = ur.head.nas_name
          val nasNameLocked = s"u_${id + 1_000_000}"
          val res = np.put(
            s"/user/id/$nasid",
            Json.obj(
              "username" -> nasNameLocked,
              "locked" -> true,
              "sudo_commands" -> Json.arr(s"mv /mnt/datas/home/$nasName /mnt/datas/home/$nasNameLocked")
            )
          )
          res.map { r =>
            if (r.body.length < 8) {
              r.body.toInt == nasid
            } else {
              logger.error(r.body)
              false
            }
          }
        }
    }
  }

  def userDelete = {}


  def userInfo(accId: Int) = {
    nasRepo.userGetNasUser1(accId).flatMap { r =>
      if (r.nonEmpty ) {
        if (r.head._3.nonEmpty) {
          nasRepo.nasGroupFindBy(r.head._4.getOrElse(0)).map(group =>
            Array(r.head._3.get, "readonly style=background-color:#eee;", group.pname, ""))
        } else {
          val mobile = r.head._2
          val name = r.head._1
          val py = pinyin(name)
          val defaultname = if (py.length < 6) py + mobile.takeRight(4) else py
          logger.debug(accId.toString)
          nasRepo.userDefaultNasGroup(accId).map(g => {
            logger.debug(if (g.isEmpty) "userInfo group not found" else g.head.toString)
            Array(defaultname, "", if (g.isEmpty) "默认组" else g.head.pname, "")
          })
        }
      }else{
        Future(Array[String]("","","",""))
      }
    }
  }

  def userInfo1(accId: Int) = {
    nasRepo.userGetNasUser1(accId).flatMap { r =>
      if (r.nonEmpty && r.head._3.nonEmpty) {
        nasRepo.nasGroupFindBy(r.head._4.getOrElse(0)).map(group =>
          Array(r.head._3.get, "readonly style=background-color:#eee;", group.pname, ""))
      } else {
        val mobile = r.head._2
        val name = r.head._1
        val py = pinyin(name)
        val defaultname = if (py.length < 6) py + mobile.takeRight(4) else py
        logger.debug(accId.toString)
        Future(Array(defaultname, "", "", ""))
      }
    }
  }

  def userIsAdmin(uid: Int) = {
    nasRepo.userIsAdmin(Some(uid))
      .map { user =>
        val is_admin = PermissionAccess.check(user.head.permission, "admin")
        if (user.nonEmpty && is_admin) true else false
      }
  }

  case class User(id: Int, nas_id: Int, nas_name: String, locked: Boolean)

  private def userGetFromNas = {
    val users = ArrayBuffer[User]()
    val res = np.get("/user", "")
    res.map(r => {
      val i = r.json.as[JsArray].value.iterator
      while (i.hasNext) {
        val user = i.next().as[JsObject].value
        // {"id":28,"uid":8675309,"username":"media","unixhash":"*","smbhash":"*","home":"/var/empty","shell":"/usr/sbin/nologin","full_name":"Media User","builtin":true,"smb":false,"password_disabled":false,"locked":false,"sudo":false,"microsoft_account":false,"attributes":{},"email":null,"group":{"id":35,"bsdgrp_gid":8675309,"bsdgrp_group":"media","bsdgrp_builtin":true,"bsdgrp_sudo":false,"bsdgrp_smb":false},"groups":[],"sshpubkey":null,"local":true,"id_type_both":false}
        users.append(User(
          id = user("uid").as[Int] - 1_000_000,
          nas_id = user("id").as[Int],
          nas_name = user("username").as[String],
          locked = user("locked").as[Boolean]
        ))

      }
      users.toSet
    })
  }

  private def userGetFromDb = {
    val users = ArrayBuffer[User]()
    nasRepo.nasUserAll.map {
      r => {
        val i = r.iterator
        while (i.hasNext) {
          val group = i.next()
          users.append(User(
            id = group.id,
            nas_id = group.nas_id,
            nas_name = group.nas_name,
            locked = group.locked
          ))
        }
        users.toSet
      }
    }
  }

  def userCheck = {
    userGetFromNas.flatMap { nas => {
      userGetFromDb.map { db => {
        val publicSet = nas & db
        val nasOnlySet = nas &~ publicSet
        val dbOnlySet = db &~ publicSet
        logger.debug(s"${publicSet.mkString(",")}\n${nasOnlySet.mkString(",")}\n${dbOnlySet.mkString(",")}")
        // nas only
        val nasonly = nasOnlySet.filter(_.id > 0).filter(_.id < 1_000_000)
        logger.debug(nasonly.mkString(","))
        nasonly.foreach { f => nasRepo.nasUserIdUpdate(f.id, f.nas_id) }
        // db only
        val dbonly = dbOnlySet //.filter(_.nas_id == 0)
        dbonly.foreach(f =>
          nasRepo.userDefaultNasGroup(f.id).map(g =>
            userCreateToNas(f.id, f.nas_name, "Wy" + java.lang.Math.random.toString.hashCode.toString, if (g.isEmpty) np.freeNASConfig.gid else g.head.nas_id)
          )
        )
        dbonly.mkString(",")
      }
      }
    }
    }
  }

  def setGroupId(id: Int, gid: Int) = {
    nasRepo.setGroupId(id, gid)
  }

}
